Conversation
bae1db9 to
5a5af1e
Compare
Add a structured build report that captures per-module and per-mojo execution results, timing, log events, and failures as a JSON file (target/build-reports/) at the end of every build. Part 2 of the #12572 split. Builds on the logging foundation from PR #12694 (LogEvent, LogLevel, LogEventSink). New API interfaces: - BuildReport: root report with metadata, modules, failures, problems - BuildStatus: SUCCESS/FAILURE/SKIPPED enum - ModuleReport: per-module results with mojo list - MojoReport: per-mojo execution with captured log events - FailureReport: exception details and stack traces Implementation: - BuildReportCollector: EventSpy that tracks lifecycle events and captures log output via LogEventSink, routing events to mojo/module/build-level buffers using thread-based tracking - BuildReportJsonWriter: zero-dependency JSON serializer - Atomic file writes with timestamped files and latest symlink - Thread-safe for parallel builds (-T) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
e64db31 to
de8044a
Compare
Add a structured build report that captures per-module and per-mojo execution results, timing, log events, and failures as a JSON file (target/build-reports/) at the end of every build. Part 2 of the #12572 split. Builds on the logging foundation from PR #12694 (LogEvent, LogLevel, LogEventSink). New API interfaces: - BuildReport: root report with metadata, modules, failures, problems - BuildStatus: SUCCESS/FAILURE/SKIPPED enum - ModuleReport: per-module results with mojo list - MojoReport: per-mojo execution with captured log events - FailureReport: exception details and stack traces Implementation: - BuildReportCollector: EventSpy that tracks lifecycle events and captures log output via LogEventSink, routing events to mojo/module/build-level buffers using thread-based tracking - BuildReportJsonWriter: zero-dependency JSON serializer - Atomic file writes with timestamped files and latest symlink - Thread-safe for parallel builds (-T) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a structured build report that captures per-module and per-mojo execution results, timing, log events, and failures as a JSON file (target/build-reports/) at the end of every build. Part 2 of the #12572 split. Builds on the logging foundation from PR #12694 (LogEvent, LogLevel, LogEventSink). New API interfaces: - BuildReport: root report with metadata, modules, failures, problems - BuildStatus: SUCCESS/FAILURE/SKIPPED enum - ModuleReport: per-module results with mojo list - MojoReport: per-mojo execution with captured log events - FailureReport: exception details and stack traces Implementation: - BuildReportCollector: EventSpy that tracks lifecycle events and captures log output via LogEventSink, routing events to mojo/module/build-level buffers using thread-based tracking - BuildReportJsonWriter: zero-dependency JSON serializer - Atomic file writes with timestamped files and latest symlink - Thread-safe for parallel builds (-T) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a structured build report that captures per-module and per-mojo execution results, timing, log events, and failures as a JSON file (target/build-reports/) at the end of every build. Part 2 of the #12572 split. Builds on the logging foundation from PR #12694 (LogEvent, LogLevel, LogEventSink). New API interfaces: - BuildReport: root report with metadata, modules, failures, problems - BuildStatus: SUCCESS/FAILURE/SKIPPED enum - ModuleReport: per-module results with mojo list - MojoReport: per-mojo execution with captured log events - FailureReport: exception details and stack traces Implementation: - BuildReportCollector: EventSpy that tracks lifecycle events and captures log output via LogEventSink, routing events to mojo/module/build-level buffers using thread-based tracking - BuildReportJsonWriter: zero-dependency JSON serializer - Atomic file writes with timestamped files and latest symlink - Thread-safe for parallel builds (-T) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
8baa65a to
02ac855
Compare
gnodet
left a comment
There was a problem hiding this comment.
Well-designed foundational logging infrastructure. The structured LogEvent API, JUL handler, and Log API enhancements provide a solid base for the build report and console modes PRs. A few issues noted below.
Also noted:
- Good catch fixing
warn(Supplier<String>, Throwable)callinglogger.info()instead oflogger.warn(). - The logger name change from
getFullGoalName()togetImplementation()(FQCN) enables proper hierarchical SLF4J level configuration but is a behavioral change — worth mentioning in release notes for users who configured logging by short-form names. - No unit tests were added for the new functionality (MavenJulHandler, DefaultLogEvent, StackWalker metadata capture, LogSink contract). Given this is foundational for the entire logging pipeline, targeted tests would increase confidence.
This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.
Claude Code on behalf of gnodet
ascheman
left a comment
There was a problem hiding this comment.
Really solid foundation — the three-path convergence (Log API / SLF4J / JUL) onto one structured LogEvent is clean, and preserving the LogRecord metadata the stock SLF4JBridgeHandler drops is a genuine improvement. Nice catch on the warn(Supplier, Throwable) → logger.info() bug.
A few things worth a look before this becomes the base of the 7-PR chain — one API-compat question, one fork-context correctness question, one perf note, and some small nits. Nothing structural.
On tests (echoing the earlier note): the two I'd most want are a regression test asserting warn(Supplier, Throwable) actually logs at WARN, and a table test for the JUL→SLF4J level mapping (esp. FINEST→TRACE and CONFIG→INFO). Given the ThreadLocal/StackWalker plumbing, those would lock down the easy-to-regress bits.
gnodet
left a comment
There was a problem hiding this comment.
Well-designed logging infrastructure foundation with clean three-path convergence (Log API, JUL, SLF4J). The bug fix for warn(Supplier, Throwable) calling logger.info() is confirmed correct.
Findings:
-
[medium]
sequenceNumber()javadoc/contract mismatch —LogEvent.sequenceNumber()javadoc says@return the sequence number, always non-negativebut the default implementation returns-1. The sibling methodthreadId()correctly documentsor -1 if unavailablein its@returntag. ThesequenceNumber()javadoc should follow the same pattern for consistency. -
[medium] Inconsistent
formattedMessageformat between JUL and SLF4J — When aLogSinkis installed, JUL events'formattedMessageis built byformatForConsole()which produces a minimal[LEVEL] messagestring, while SLF4J events produce a full formatted string with timestamps, thread names, and logger names viaMavenBaseLogger.innerHandleNormalizedLoggingCall(). The practical impact is limited since the cleanmessage()field is available for consumers who need consistent content, but inSimpleBuildEventListener.projectLogMessage()which usesformattedMessage()for console output, JUL events will look noticeably different from SLF4J events. -
[low] Log4j2/Logback backend removal — The removal of
Log4j2Configuration,LogbackConfiguration, and thelogback-classicdependency means Maven no longer supports these as alternative SLF4J backends. This is intentional for the Maven 4.x logging redesign, but warrants mention in release notes for users who embedded Maven with a custom logging backend.
This review was generated by an AI agent (Claude Code) and may contain inaccuracies. Please verify all suggestions before applying.
Claude Code on behalf of Guillaume Nodet
gnodet
left a comment
There was a problem hiding this comment.
Well-architected logging foundation PR. Clean design with proper ThreadLocal management, volatile concurrency handling, and good layering (API → impl → collector). A few items to address:
High severity:
-
API contract contradiction (
LogEvent.javaline 188):sequenceNumber()Javadoc says "@return the sequence number, always non-negative" but the default implementation returns-1. Compare withthreadId()which correctly documents "or -1 if unavailable". This is a public API interface marked@Experimental/@since 4.1.0— the Javadoc should match the actual contract. -
No test coverage: 1000+ lines of foundational code across 22 files with zero test files.
LogEvent/DefaultLogEvent,MavenJulHandler(249 lines),DefaultLog.withMetadata/trace/child,LogSinkinterface,ProjectBuildLogAppenderstructured event creation, and the mojo MDC lifecycle are all untested. The PR description mentions "580 tests pass" but these are all pre-existing tests.
Medium severity:
-
StackWalker overhead (
DefaultLog.javaline 649):withMetadata()callsStackWalker.walk()on every log call for enabled levels. While trace/debug are typically disabled and info/warn/error are low-volume, plugins logging many INFO/WARN messages will pay the 1-5μs per-call cost. -
Logger name change (
DefaultBuildPluginManager.javaline 128): Logger name changed fromgetFullGoalName()(e.g., "compiler:compile") togetImplementation()(e.g., "org.apache.maven.plugins.compiler.CompilerMojo"). Intentional for proper hierarchical SLF4J configuration, but a user-visible behavior change that could break existing SLF4J level configurations. -
Dead code for future PR (
ProjectBuildLogAppender.javaline 130):reportCapturevolatile field and setter are infrastructure for PR #12695 (build report). Currently unused in this PR — consider adding a brief comment noting the intent.
Low severity:
-
setMojoId(null)is called beforedelegate.mojoSucceeded/mojoFailedcallbacks, inconsistent with theforkSucceeded/forkFailedpattern where cleanup happens after the delegate. -
The bug fix changing
logger.info()tologger.warn()inwarn(Supplier<String>, Throwable)is correct and important. 👍
The removal of Logback/Log4j2 support is a significant architectural decision — worth explicit mention in release notes since users plugging in alternative SLF4J backends will lose that ability.
This review was generated by an AI agent (Claude Code) and may contain inaccuracies. Please verify all suggestions before applying.
Claude Code on behalf of Guillaume Nodet
02ac855 to
812a842
Compare
Review feedback addressedAll 8 review comments from @gnodet and @ascheman have been addressed in the latest force-push. Summary of changes: Bug fixes
Design improvements
Tests added
All 6 downstream PRs (#12695, #12697, #12698, #12699, #12702, #12714) have been rebased onto the updated commit. |
812a842 to
84568d2
Compare
Apply review fixes from #12694 to align the backport: - Log.java: make all 6 trace methods default (no-ops) to prevent AbstractMethodError for existing third-party Log implementors. isTraceEnabled() returns false by default. - ProjectBuildLogAppender: add FORKING_MOJO_ID ThreadLocal mirroring the existing FORKING_PROJECT_ID pattern. When setMojoId(null) is called, the forking mojo's ID is restored instead of clearing. - LoggingExecutionListener: save current mojoId in forkStarted(), clear forking mojoId in forkSucceeded/forkFailed. Fix cleanup ordering in mojoSucceeded/mojoFailed — delegate runs first, then MDC is cleared. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Backport Log API enhancements and mojo MDC to 4.0.x
Backport four Log-related improvements from master to the 4.0.x branch
for inclusion in rc-7:
1. Log.trace() — new trace level (maps to SLF4J TRACE / JUL FINEST)
to separate Maven core internals from user-facing debug messages.
Currently -X floods debug output with resolver/interpolation details
that drown user-relevant diagnostics.
2. Log.child(name) — creates a sub-logger with an independently
filterable name (e.g. "CompilerMojo.diagnostics"), letting plugin
sub-components log under their own namespace.
3. Logger name alignment — Maven 4 Log now uses the mojo implementation
class name (e.g. "org.apache.maven.plugins.compiler.CompilerMojo")
instead of the goal name ("compiler:compile"). This matches what
Maven 3 mojos already use and enables standard SLF4J hierarchical
level configuration.
4. Mojo MDC propagation — sets "maven.mojo.id" (prefix:goal@executionId)
in the SLF4J MDC during mojo execution. All log messages — including
those arriving through the JUL-to-SLF4J bridge — now carry mojo
context, available to any SLF4J appender via %X{maven.mojo.id}.
Also fixes a pre-existing bug in DefaultLog where warn(Supplier, Throwable)
incorrectly delegated to logger.info() instead of logger.warn().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add isXxxEnabled() guards to Throwable-only log overloads
Align with master by wrapping the five xxx(Throwable) overloads
in level-enabled checks, avoiding unnecessary method calls and
empty string construction when the level is disabled.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Address review: default trace methods and fork-aware mojoId
Apply review fixes from #12694 to align the backport:
- Log.java: make all 6 trace methods default (no-ops) to prevent
AbstractMethodError for existing third-party Log implementors.
isTraceEnabled() returns false by default.
- ProjectBuildLogAppender: add FORKING_MOJO_ID ThreadLocal mirroring
the existing FORKING_PROJECT_ID pattern. When setMojoId(null) is
called, the forking mojo's ID is restored instead of clearing.
- LoggingExecutionListener: save current mojoId in forkStarted(),
clear forking mojoId in forkSucceeded/forkFailed. Fix cleanup
ordering in mojoSucceeded/mojoFailed — delegate runs first, then
MDC is cleared.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Address review: add DefaultLogTest and clear MDC on mojoSkipped
- Add DefaultLogTest with 5 tests: warn/supplier regression,
trace delegation, trace no-op guard, child() sub-logger,
and default trace methods (AbstractMethodError prevention).
- Clear mojo MDC in mojoSkipped() to prevent stale mojo context
from leaking into subsequent log messages.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of commit 473a044526 (force-push rebase onto master, 2026-09-18).
Both findings from the previous REQUEST_CHANGES review are confirmed addressed:
-
@param contenttag restored —trace(Supplier<String> content)now has@param content the message supplier, consistent with all other supplier-based overloads in the interface. -
@param content+@param errortags restored —trace(Supplier<String> content, Throwable error)now carries both@paramtags, matching the other supplier+throwable overloads.
New code in this commit reviewed and clean:
LogEvent.projectId()andmojoId()— properly@Nullable-annotated default methods with accurate Javadoc;DefaultLogEventrecord components wired correctly inProjectBuildLogAppender.accept()viaMOJO_ID.get().DefaultLog.withMetadata()refactor — the fast path (else { logAction.run(); }when report capture is off) correctly avoids all ThreadLocal and StackWalker cost.LookupInvokerdrain reorg — drain now happens inactivateLogging()aftercreateTerminal()has installedProjectBuildLogAppender; the ordering (drain into newSlf4jLoggerbeforecontext.logger = logger) is correct. ThefailOnSeveritymessage accumulates in the old logger and drains into the new one in the same call — no double-replay risk.configureLogging()earlySEVEREguard —MavenJulHandler.install()+Level.SEVEREon the JUL root logger in quiet mode is correctly placed beforecreateTerminal()runs.MavenJulHandlerTestcomment corrected to accurately describe the reentrancy simulation.
Two earlier non-blocking observations remain open (DefaultLog.child(name) blank-name guard; LogEvent.formattedMessage() Javadoc multi-line throwable note) — neither blocks this PR.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of commit 473a0445 (force-push rebase, 2026-09-18).
Both findings from the CHANGES_REQUESTED review on 6ccbc27a are confirmed fixed:
-
@param content/@param errortags — restored inLog.javafor bothtrace(Supplier<String>)(line 93) andtrace(Supplier<String>, Throwable)(line 104). Matches every other supplier overload in the interface. ✅ -
version.*→lifecycle.*property rename — consistent acrosspom.xmlproperty declarations,plugin-versions.propertieskeys/values, andPluginVersions.version()key construction. ✅
New additions in this commit:
-
Windows quiet-mode JUL fix —
configureLogging()now eagerly installsMavenJulHandlerand sets JUL root toSEVEREin quiet mode, beforecreateTerminal()runs. This closes the race window where JLine terminal-init JUL events (fired betweenconfigureLogging()andactivateLogging()) leaked into quiet output. The approach is sound: SLF4J is already bootstrapped at this point (viaLoggerFactory.getILoggerFactory()earlier inconfigureLogging()), so the install doesn't trigger thecomputeIfAbsentreentrancy flood that installing before SLF4J bootstrap would.activateLogging()idempotently skips re-installation viaisInstalled(). ✅ -
LogEventAPI moved tomaven-api-core(org.apache.maven.api.build.report) —LogEventinterface promoted frommaven-internalto public API.projectId()andmojoId()default methods added.DefaultLogEventrecord updated with matching fields, populated inProjectBuildLogAppender.accept()from thePROJECT_ID/MOJO_IDThreadLocals. TheLogEventis now self-contained with execution context without requiring callers to correlate against lifecycle events. ✅ -
DefaultLog.withMetadata()optimization — ThreadLocal + StackWalker cost now fully avoided whenhasReportCapture()is false. The branch is clean: report-capture path sets and removes the ThreadLocal in try/finally; non-capture path callslogAction.run()directly.ProjectBuildLogAppender.accept()correctly falls through to null source metadata whengetLogApiMetadata()returns null (no-capture case). ✅ -
pendingEarlyLogsremoval — drain now happens directly inactivateLogging()aftercreateTerminal()has installed theProjectBuildLogAppenderlogSink. The field is gone fromLookupContext. Simpler and correct — no more dead-code drain increateTerminal(). ✅ -
MavenJulHandlerTestcomment fix — comment now accurately describes the test: simulating re-entrance by settingIN_PUBLISH=truedirectly, rather than the misleading "install a custom SLF4J logger that fires a second JUL event". ✅
One low-severity observation (not blocking):
In configureLogging(), there is a tiny window between MavenJulHandler.install() (which leaves JUL root level at the JUL default, typically INFO) and the subsequent LogManager.getLogManager().getLogger("").setLevel(Level.SEVERE). Any JUL event at INFO or WARNING fired in that two-line gap on a concurrent thread would pass through MavenJulHandler.publish(). In practice this is not exploitable (the startup is effectively single-threaded at this point and no concurrent threads are emitting JUL events yet), but the two lines could be reordered — set level first, then install — to eliminate even the theoretical gap. Not worth a follow-up PR; just noting it for completeness.
APPROVED. Both prior findings addressed, new changes correct.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of commit 473a0445 (final squash, 2026-09-18T16:59:37Z).
The two @param tags flagged in the previous CHANGES_REQUESTED (on 6ccbc27a) are confirmed present in this squash:
trace(Supplier<String> content)—@param content the message supplierat line 91 ✅trace(Supplier<String> content, Throwable error)— both@param contentand@param errorat lines 101-102 ✅
All prior findings from previous review cycles are addressed. No new findings.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
473a044 to
98915c1
Compare
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of commit 98915c1a (squash-rebase, 2026-09-19).
The CHANGES_REQUESTED finding from the previous review (@param content and @param error tags dropped in squash on trace(Supplier<String>) / trace(Supplier<String>, Throwable)) is confirmed fixed — both tags are present. The other significant changes in this squash are also solid: projectId()/mojoId() propagation through DefaultLogEvent is correct, the drain moved to activateLogging() fixes the dead-code bug cleanly, and Level.SEVERE in quiet mode correctly matches Slf4jConfiguration.Level.ERROR.
Two low-severity issues remain open.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of commit 98915c1a (force-push squash, 2026-09-19).
Prior CHANGES_REQUESTED resolved: Both @param content tags in Log.java (for trace(Supplier<String>) at line 93 and trace(Supplier<String>, Throwable) at line 104) are present — the squash-rebase regression is fixed.
Two low-priority findings remain from the earlier review thread that were deferred due to the dedup gate firing. Raising them now.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of commit f68c43db (force-push rebase onto master, 2026-09-20).
All previously requested changes are now resolved: the two @param tags dropped in the 6ccbc27a squash (@param content on trace(Supplier<String>) and @param content/@param error on trace(Supplier<String>, Throwable)) are present in the current HEAD, and the prior low-priority findings from 98915c1a (the message() @return copy-paste and the hollow logApiMetadataIsClearedAfterCall() test) are both fixed.
One new finding below.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of commit 3f5b4393 ("Address review: call isLoggable() in MavenJulHandler.publish() per Handler contract") and d7f564ab ("Address review: fix LogEvent.message() @return Javadoc, strengthen logApiMetadata test").
Prior finding addressed: ✅ isLoggable(record) is now called in MavenJulHandler.publish() before the re-entrancy guard — correctly honouring the java.util.logging.Handler contract. Placement is right: null check → isLoggable → re-entrancy guard.
New finding (one): The rebase did not correctly pick up master's lifecycle.* property rename, causing the branch to diverge from master on three files.
[high] Bad rebase: branch reverts lifecycle.* rename already on master
Master (current tip) has renamed the lifecycle plugin version properties from version.maven-*-plugin to lifecycle.maven-*-plugin across three files:
impl/maven-core/pom.xml— property declarationsimpl/maven-core/src/main/resources/org/apache/maven/lifecycle/plugin-versions.properties— property keysimpl/maven-core/src/main/java/org/apache/maven/lifecycle/PluginVersions.java— key lookup ("lifecycle." + pluginArtifactId)
This branch still has the old version.* names in all three files. When merged, the PR would revert that rename, causing a key mismatch at startup: PluginVersions.java looks up version.maven-clean-plugin, but the properties file (coming from master's version) would have lifecycle.maven-clean-plugin — producing IllegalArgumentException: No default version defined for maven-clean-plugin at class initialisation time.
In addition, the branch carries plugin versions from before the rename (e.g. maven-clean-plugin 3.4.0 vs master's 3.5.0).
The fix is a clean rebase: let git rebase bring in master's version of these three files rather than keeping the branch's old content. These files are unrelated to this PR's stated purpose and should be transparent to the rebase.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
|
Fixed in cbc1d60: restored master's lifecycle plugin versions in |
|
Fixed in |
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of commits cbc1d608 ("fix: restore master's lifecycle plugin versions in pom.xml (rebase regression)") and bbff94a9 ("fix: update lifecycle plugin versions in pom.xml to match master").
Prior CHANGES_REQUESTED resolved: ✅ The bad-rebase finding from review 5261571175 is fully addressed. All three files called out are now correct:
impl/maven-core/pom.xml— all 13lifecycle.maven-*-pluginproperty values now match master exactly (verified blob-level: PR and master are identical line-for-line)impl/maven-core/src/main/resources/org/apache/maven/lifecycle/plugin-versions.properties— same blob hash as master (671212e9f2ce)impl/maven-core/src/main/java/org/apache/maven/lifecycle/PluginVersions.java— same blob hash as master (af623cf7d35e)
The version.maven-*-plugin comment in the <pluginManagement> section (line 284) is pre-existing on master and was not introduced by this PR; no action needed there.
The two fix commits are purely mechanical (13 version bumps in pom.xml + 1-line comment alignment) — no logic changes. The core logging files (LogEvent, LogLevel, Log, DefaultLogEvent, MavenJulHandler, MavenBaseLogger, MavenSimpleLogger) are unchanged from the previously approved state.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet
left a comment
There was a problem hiding this comment.
Re-review of commits cbc1d608db ("fix: restore master's lifecycle plugin versions in pom.xml (rebase regression)") and bbff94a91c ("fix: update lifecycle plugin versions in pom.xml to match master").
Prior CHANGES_REQUESTED finding — confirmed resolved:
The blocking rebase regression flagged on 3f5b4393 is fixed. Both commits restore the lifecycle.maven-*-plugin property names and their current master values across all 13 lifecycle plugin version properties in impl/maven-core/pom.xml:
maven-clean-plugin: 3.4.0 → 3.5.0 ✅maven-compiler-plugin: 3.13.0 → 3.16.0 ✅maven-deploy-plugin: 3.1.3 → 3.2.0 ✅maven-ear-plugin: 3.3.0 → 3.4.0 ✅maven-ejb-plugin: 3.2.1 → 3.3.0 ✅maven-install-plugin: 3.1.3 → 3.2.0 ✅maven-jar-plugin: 3.4.2 → 3.5.1 ✅maven-plugin-plugin: 3.15.1 → 3.16.0 ✅maven-rar-plugin: 3.0.0 → 3.1.0 ✅maven-resources-plugin: 3.3.1 → 3.5.0 ✅maven-site-plugin: 3.21.0 → 3.22.0 ✅maven-surefire-plugin: 3.5.2 → 3.6.0 ✅maven-war-plugin: 3.4.0 → 3.5.1 ✅
The plugin-versions.properties and PluginVersions.java key scheme (lifecycle.*) is consistent with the updated POM properties. The ExceptionInInitializerError at class load time will no longer occur. The pluginManagement comment saying "version.maven-*-plugin" is a pre-existing stale comment on master (not introduced by this PR) — not blocking.
All prior findings across the full review history remain addressed. No new issues.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of commits cbc1d608db ("fix: restore master's lifecycle plugin versions in pom.xml (rebase regression)") and bbff94a91c ("fix: update lifecycle plugin versions in pom.xml to match master").
Prior CHANGES_REQUESTED finding — resolved.
The rebase regression that introduced old lifecycle plugin versions (clean/3.4.0, compiler/3.13.0, etc.) is fully fixed. All three affected files now match master exactly:
- ✅
impl/maven-core/pom.xml— all 13lifecycle.maven-*-pluginproperties updated to master versions (clean/3.5.0, compiler/3.16.0, deploy/3.2.0, ear/3.4.0, ejb/3.3.0, install/3.2.0, jar/3.5.1, plugin/3.16.0, rar/3.1.0, resources/3.5.0, site/3.22.0, surefire/3.6.0, war/3.5.1). Blob diff vs master: none. - ✅
plugin-versions.properties— identical blob to master (671212e9).${lifecycle.maven-*-plugin}filter references are correct. - ✅
PluginVersions.java— identical blob to master. NoExceptionInInitializerErrorrisk at class init.
All core logging foundation files (LogEvent, LogLevel, Log, DefaultLogEvent, DefaultLog, MavenJulHandler, MavenBaseLogger, MavenSimpleLogger, ProjectBuildLogAppender, SimpleBuildEventListener) are unchanged from the previously APPROVED state at 3f5b4393.
CI is pending — no functional risk identified in this incremental delta.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
bbff94a to
707b355
Compare
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of commit 707b3555 (squash rebase onto master, 2026-09-20).
Three correctness regressions in this squash. The logging foundation code itself (LogEvent API, MavenJulHandler, ProjectBuildLogAppender restructuring, DefaultLog metadata) is solid and unchanged from the previously APPROVED state. The issues are all in the bundled fixes.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of squash commit 707b3555 (force-push rebase onto master, 2026-09-20).
All prior findings addressed in this squash:
@param contentmissing on Supplier-basedtrace()methods (CHANGES_REQUESTED on6ccbc27a) — fixed:@param content the message suppliernow present on bothtrace(Supplier<String>)andtrace(Supplier<String>, Throwable).LogEvent.message()@returncopy-paste — fixed:@return the log message, never {@code null}now matches the body description.pendingEarlyLogsdead drain increateTerminal()(raised on3752425804) — resolved with a cleaner approach:activateLogging()now drains directly into the SLF4J logger aftercreateTerminal()has already installedProjectBuildLogAppender. NopendingEarlyLogsfield needed.- Quiet-mode JUL root level
WARNING → SEVERE— fixed inconfigureLogging(), correctly usingLevel.SEVEREfor the quiet-mode pre-guard. isLoggable(record)inMavenJulHandler.publish()— present, correct.IN_PUBLISHreentrancy guard — present with correcttry/finallycleanup.
One low-severity finding on the reentrancy test.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of commits 2272eae465 ("fix: address review — restore concurrency guard in MavenProject.getArtifacts(), cleanup in finally, ancestor-dir guard in PluginUpgradeStrategy") and b0a1155e82 ("Address review: replace IN_PUBLISH reflection in test with package-private hook").
All four outstanding findings are confirmed resolved:
-
✅ MavenProject.getArtifacts() partial-set visibility — intermediate
resultvariable restored;artifactsfield is now assigned atomically after the filter loop completes, eliminating the concurrent partial-set observation window introduced by the squash. -
✅ PluginUpgradeStrategy.doApply() tempDir leak on exception —
tempDirdeclared before the try, cleanup moved tofinallywith a null guard. Any exception path (includingcreateTempProjectStructure()itself) now reliably triggers cleanup. -
✅ upgradePropertyVersion() sibling/child POM mutation — ancestor-directory guard restored:
currentDir.startsWith(candidateDir)correctly restricts the cross-POM property search to ancestor directories only (a directory is an ancestor iff the current directory starts with its path). -
✅ publishIsReentrantSafe() reflection brittleness —
setInPublishForTest(boolean)package-private hook added. Test and production code are in the same package (org.apache.maven.slf4j), so the hook is accessible without reflection. The test is also meaningfully simplified — unnecessary root-handler save/restore noise eliminated, FQCN replaced with proper import.
One trivial nit (informational): The setInPublishForTest Javadoc says @param inPublish ... {@code false} (or pass {@code null} via the {@code remove} path) — but the parameter is a primitive boolean, so null cannot be passed. The remove() semantics are an implementation detail of the false branch, not a separate call convention. The Javadoc is harmless but slightly misleading; worth fixing in a follow-up if there is one.
No new issues found. This PR is ready to merge.
This review was generated by an AI agent, Hermès.
gnodet
left a comment
There was a problem hiding this comment.
Re-review of commits 2272eae4 ("fix: address review — restore concurrency guard, cleanup in finally, ancestor-dir guard") and b0a1155e ("Address review: replace IN_PUBLISH reflection in test with package-private hook").
All three CHANGES_REQUESTED findings from 707b3555 are confirmed addressed:
-
Concurrency regression in
MavenProject.getArtifacts()— ✅ Restored. The localresultvariable is back;artifactsis only assigned after the loop completes. Concurrent readers can no longer observe a partially-populated set. The fix is identical to the original pre-squash code. -
Temp-directory leak in
PluginUpgradeStrategy.doApply()— ✅ Fixed.tempDiris now declared before thetryblock (initialized tonull), andcleanupTempDirectory()is in afinallywith a null guard. Any uncaught exception betweencreateTempProjectStructure()and the old cleanup call no longer leaks the temp directory. -
Sibling-POM mutation regression in
upgradePropertyVersion()— ✅ Fixed. The ancestor-directory guard is restored:currentPomPath/currentDirare resolved from thepomMap, and the per-candidate check!currentDir.startsWith(candidateDir)skips non-ancestor POMs. Sibling and child POMs are correctly excluded from cross-POM property searches.
b0a1155e — IN_PUBLISH reflection replaced with package-private hook:
The refactor is clean. MavenJulHandler.setInPublishForTest(boolean) is package-private (same package as the test), has accurate Javadoc, and correctly uses IN_PUBLISH.set(Boolean.TRUE) / IN_PUBLISH.remove() mirroring what publish() does internally. The test is simplified: the unnecessary JUL root-handler save/restore scaffolding is gone, the throws Exception declaration is removed, and the assertion is now self-documenting. If IN_PUBLISH is ever renamed, the test will fail at compile time rather than at runtime with NoSuchFieldException.
No new issues. All prior findings from the full review history are addressed. The logging foundation is solid and ready.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of commit 218c57feec ("fix: remove extra blank line to stay within 2000-line checkstyle limit").
Single-line cosmetic change — removes the blank line between this.artifacts = artifacts; and // flush the calculated artifactMap in setArtifacts(). No logic change.
Prior APPROVE on b0a1155e82 stands. PR is ready to merge.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of commit 218c57fe (force-push rebase onto master, 2026-09-20).
Two real issues found. The previous CHANGES_REQUESTED on @param tags is resolved — both @param content the message supplier and @param error the error that caused this log are present in Log.java lines 91–104 of this commit. The logging foundation code (MavenJulHandler, ProjectBuildLogAppender, DefaultLog, LogEvent API) is unchanged from the previously approved state.
Finding 1 (high): PomInlinerTransformer — CI-friendly version regression re-introduced
Finding 2 (medium): PluginUpgradeStrategy.analyzePluginsUsingEffectiveModels — findCommonRoot called per-module inside the loop (O(n²))
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of commit 628c65fa ("Address review: restore GH-13192 PomInliner pomProperties fallback, hoist findCommonRoot out of loop").
All four CHANGES_REQUESTED findings from the previous review (707b3555) are confirmed fixed:
- ✅
MavenProject.getArtifacts()concurrency guard — localresultvariable restored;artifacts = resultassigned atomically after the loop. No concurrent reader can observe a partially-populated set. - ✅
PluginUpgradeStrategytemp-dir leak —cleanupTempDirectory()moved tofinallyblock; cleanup is guaranteed even when an exception is thrown. - ✅ Sibling-POM mutation guard — ancestor-directory restriction (
currentDir.startsWith(candidateDir)) correctly limits cross-POM property searches to parent directories only. - ✅
MavenJulHandlerTestreflection — replaced with package-privatesetInPublishForTest()hook; test no longer breaks onIN_PUBLISHfield rename.
Also confirmed in this squash:
- ✅
findCommonRoothoisted out of the per-module loop — O(N²) → O(N). - ✅
MavenJulHandler.publish()now callsisLoggable(record)per theHandlercontract. - ✅
PomInlinerTransformerCI-friendly fallback: GAV-keyedpomPropertiescache correctly propagates POM-defined${revision}throughinjectTransformedArtifacts→replacePomwithout cross-module collision. IT testMavenITgh13192PomInlinerCiFriendlyPropertyTestcovers the regression.
One gap: missing negative test for the sibling-mutation guard.
upgradePropertyVersion() has an ancestor-directory guard (!currentDir.startsWith(candidateDir)) that prevents sibling POMs from being mutated when they happen to define the same property. The existing shouldUpgradePluginWithPropertyVersionInParentPom test covers the success path (parent gets upgraded) and shouldNotWarnWhenPropertyAlreadyAtTargetVersion covers the no-change path, but there is no test that verifies a sibling POM at the same directory level is not mutated.
Without this test, a future refactor that removes the guard will silently regress to the original bug. The scenario to add:
root/pom.xml— noexec.maven.versionpropertyroot/module-a/pom.xml— defines<exec.maven.version>3.1.0</exec.maven.version>, uses${exec.maven.version}root/module-b/pom.xml— also defines<exec.maven.version>3.1.0</exec.maven.version>independently
After doApply: module-a’s property upgraded to 3.5.0. Module-b’s property must remain untouched (it was not the target of the upgrade).
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of commits 707b355→628c65fa (force-push rebase, 2026-09-20).
All prior findings are addressed:
@param content/@param errortags (CHANGES_REQUESTED, 2026-09-18): restored inLog.java— bothtrace(Supplier<String>)andtrace(Supplier<String>, Throwable)now have correct@paramtags. ✓- Concurrency guard in
MavenProject.getArtifacts():resultlocal variable restored —artifactsis only assigned after the loop completes. ✓ IN_PUBLISHreflection in test: replaced with the newsetInPublishForTest(boolean)package-private hook. ✓cleanupTempDirectory()infinallyblock: confirmed present inPluginUpgradeStrategy.doApply(). ✓- Quiet-mode JUL root level
SEVERE: confirmed inLookupInvoker.configureLogging()andactivateLogging(). ✓
Two remaining gaps:
cleanupTempDirectory()leaks theFiles.walk()stream (new finding)MavenProjectGetArtifactsTestdeleted without replacement (regression protection gap)
This review was generated by an AI agent, Hermès on behalf of @gnodet.
| } | ||
| protected void cleanupTempDirectory(Path tempDir) { | ||
| try { | ||
| Files.walk(tempDir) |
There was a problem hiding this comment.
[medium] Files.walk() stream not closed — filesystem handle leak
Files.walk() returns a Stream<Path> that holds an open directory handle until closed. The current code never closes it: if forEach throws (e.g. SecurityException from File::delete) the stream stays open until GC. In the previous code deleted by this PR, AbstractUpgradeStrategyTest used try (var walk = Files.walk(...)) correctly — that pattern should be followed here.
| Files.walk(tempDir) | |
| protected void cleanupTempDirectory(Path tempDir) { | |
| try (var walk = Files.walk(tempDir)) { | |
| walk.sorted(Comparator.reverseOrder()) | |
| .map(Path::toFile) | |
| .forEach(File::delete); | |
| } catch (Exception e) { | |
| // Best effort cleanup | |
| } | |
| } |
| assertFalse(project.getArtifacts().isEmpty()); | ||
| assertEquals(resolved.size(), project.getArtifacts().size()); | ||
| } | ||
| } |
There was a problem hiding this comment.
[medium] Concurrency regression test deleted without replacement
This file is deleted by the PR, but the concurrency fix it covers (MavenProject.getArtifacts() — restore result local variable so artifacts is only assigned after the loop completes) is still present in the production code. Deleting the test removes the guard against this regression being reintroduced silently. The test should either be retained as-is (it's a clean, self-contained stress test) or ported into a different test class, but not simply deleted.
Note that MavenProject.artifacts is still non-volatile and getArtifacts() is still unsynchronized — the result local variable prevents observers from seeing a half-populated set during construction, but two threads that both see artifacts == null will still both enter the if-block and both compute the set. Whether that's acceptable depends on the broader threading model of MavenProject, but the test at minimum ensures the final state is consistent.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of commit a471a464 (rebase onto master, 2026-09-20).
The prior CHANGES_REQUESTED finding from 6ccbc27a is confirmed addressed:
@param content/@param errortags — bothtrace(Supplier<String>)andtrace(Supplier<String>, Throwable)carry their@paramtags in the current head. The squash-rebase regression is gone.
The new commit also restores .github/workflows/update-lifecycle-plugins.yml and the versions-update profile in pom.xml — infrastructure that was merged to master in #13183/#13186 and dropped during the previous rebase. No issues with those additions.
Prior APPROVE stands. No new findings.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of commit 319c96d7 ("fix: restore AbstractUpgradeStrategy reactor pre-build and related mvnup classes from master").
This commit restores verbatim from master the prebuildReactorModels/effectiveModelCache/sharedModelBuilderSession machinery dropped during a prior rebase (introduced by #13190). No logging foundation code was changed.
Verified:
@param contentand@param errortags ontrace(Supplier<String>)andtrace(Supplier<String>, Throwable)inLog.java— both present (lines 91-104). The regression from6ccbc27ais fixed.prebuildReactorModels()correctness: root POM identified viagetNameCount()minimum on absolute paths — correct. Cache keys usetoAbsolutePath().normalize()consistently on both write (line 209) and read (line 441) sides — no path-mismatch risk.sharedModelBuilderSessionfallback inbuildEffectiveModel()when cache misses — correct; lazily created and reused somappedSourcesfrom the reactor pre-build is available for external parent resolution.- State cleanup in
finallyblock ofapply()— botheffectiveModelCacheandsharedModelBuilderSessionnulled out; singleton instances cannot leak state across invocations. ToolchainPluginStrategy: decoupled from running JDK (getRunningJdkMajor()hook removed), now purely declaration-based vialatestJdkForSourceLevel()— correct per the stated intent (act on what the project declares, not what JDK is running).AbstractUpgradeStrategyTest: solid regression test for #13190 — multi-module project with<version>omitted in child POM, verifying inference works without "version is missing" cascade.@paramremovals inAbstractUpgradeGoalprotected methods — verbatim from master, consistent with the class being an internal implementation detail.
No new issues. Prior APPROVE stands.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of commits 9d628ee9 ("fix: restore out-of-scope test changes from master"), cfa0ff42 ("fix: restore ToolchainPluginStrategyTest from master"), and 1d9c90d8 ("fix: restore out-of-scope changes in ExecutionEventLogger, Log.java, AbstractMavenTransferListener, DefaultModelBuilder").
Prior CHANGES_REQUESTED finding — resolved:
- ✅
@param contentand@param errortags ontrace(Supplier<String>)andtrace(Supplier<String>, Throwable)inLog.java— both present in the current branch.
New commits reviewed:
All three commits are pure restoration of out-of-scope code to match master — no logging-foundation logic was touched:
9d628ee9/cfa0ff42: test-only restores (PluginUpgradeStrategyTest,ParentCycleDetectionTest,ToolchainPluginStrategyTest) — correct.1d9c90d8ExecutionEventLogger: restoresgroup=2for unknown build status (correct —logReactorSummaryGroupis called for groups 0, 1, and 2) and removes the now-unusedbufferfield fromReactorSummaryRequest. Both changes match master exactly.1d9c90d8AbstractMavenTransferListener: removes redundant inline field/constructor Javadoc — fine.1d9c90d8DefaultModelBuilder: adds a better error hint for<relativePath>mismatch — correct and helpful.1d9c90d8Log.java: Javadoc improvements fortrace,debug, andchild()— accurate and cleaner.
Prior low nit (test comment in publishIsReentrantSafe()): Addressed — the comment now correctly describes the test hook mechanism.
CI is pending (initial-build + Jenkins). Logging foundation code itself is unchanged from the previously approved state.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of commit 1d9c90d (force-push rebase onto master, 2026-09-20).
The two findings from the previous REQUEST_CHANGES review are confirmed addressed:
@param contenttag restored ontrace(Supplier<String>)— present at line 93:@param content the message supplier. ✅@param contentand@param errortags restored ontrace(Supplier<String>, Throwable)— both present at lines 101-102. ✅
Three additional commits bundled in this push:
2272eae(fix: address review — restore concurrency guard in MavenProject.getArtifacts()): correct — builds into a localresultset before assigningartifacts = resultwithin thesynchronizedblock, preventing a half-populated field from being visible to concurrent readers during filtering. ✅628c65f(fix: restore GH-13192 PomInliner pomProperties fallback, hoist findCommonRoot out of loop): out-of-scope changes correctly restored from master. ✅b0a1155(Address review: replace IN_PUBLISH reflection in test with package-private hook): test comment and implementation updated correctly —publishIsReentrantSafenow usessetInPublishForTest()hook and the comment accurately describes the scenario being tested. ✅
Remaining low-severity observations from the prior APPROVE (not blocking):
Thread.currentThread().getId()is deprecated since Java 19 —@SuppressWarnings("deprecation")is present butThread.threadId()would be cleaner.DefaultLog.child()documents "must not be null or blank" butDefaultLogonly checksrequireNonNull— no blank guard.LogEvent.formattedMessage()Javadoc doesn't mention multi-line throwable rendering when an exception is present.
None of these block merge.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
…enhancements - Add LogEvent interface (maven-api-core) with projectId/mojoId context fields - Add DefaultLogEvent record (maven-core) implementing LogEvent - Add MavenJulHandler (maven-logging): bridge JUL→SLF4J for plugin logging - Enhance DefaultLog (maven-core): carry LOG_API_METADATA for mojo log capture - Add ProjectBuildLogAppender (maven-core): MDC-aware log sink feeding LogEvent stream - Suppress JLine terminal-init DEBUG logs before activateLogging in quiet mode - Gate StackWalker behind hasReportCapture() for zero overhead in normal builds - Fix warn(Supplier<String>, Throwable) incorrectly calling logger.info() - Add @PARAM tags on trace(Supplier) overloads; fix sequenceNumber() @return javadoc - Fix LogEvent.message() @return javadoc copy-paste from formattedMessage() - Strengthen logApiMetadataIsClearedAfterCall() test to exercise the remove() path
1d9c90d to
abd26c7
Compare
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of squash commit abd26c76 ("feat: logging foundation – structured LogEvent, JUL handler, Log API enhancements", 2026-09-20).
Both findings from the previous REQUEST_CHANGES review are confirmed addressed:
@param contenttag ontrace(Supplier<String> content)— present at line 91 ofLog.java, consistent with all other supplier-based overloads. ✅@param content/@param errortags ontrace(Supplier<String>, Throwable)— both present at lines 101-102. ✅
Also confirmed in this squash:
sequenceNumber()@returncorrectly readsor {@code -1} if unavailable(no longer "always non-negative").LogEvent.message()@returnjavadoc copy-paste fromformattedMessage()fixed.logApiMetadataIsClearedAfterCall()test strengthened to exercise theremove()path.
All previously raised findings are resolved. Prior approval stands.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
Summary
Master-only logging infrastructure additions, layered on top of the forward-port (#12929) of the shared Log API enhancements from 4.0.x (#12690).
What's included (master-only)
Structured LogEvent (
maven-api-core,maven-core)LogEvent/LogLevelAPI for structured log event representationsourceClassName(),sourceMethodName(),threadId()— populated for both Log API events (viaDefaultLog.withMetadata()) and JUL events (viaMavenJulHandler);null/-1for direct SLF4J loggingprojectId()andmojoId()— populated byProjectBuildLogAppenderfrom thePROJECT_ID/MOJO_IDthread-locals at event capture time, making eachLogEventself-contained without requiring callers to bracket againstmojo.started/mojo.succeededeventsCustom JUL Handler (
maven-logging)MavenJulHandlerreplacesSLF4JBridgeHandler— all JUL events always route through SLF4J so thatMavenSimpleLoggerproduces a consistentformattedMessage(with timestamp, logger name, and ANSI styling) regardless of originsourceClassName,sourceMethodName,threadId) is stashed in a ThreadLocal before the SLF4J call and read byProjectBuildLogAppenderduring the same synchronous call chain — no metadata is lostloggerNameguard per JUL spec (falls back to root logger)MavenJulHandler→ SLF4J — all converge on the same structuredLogEventStructured LogSink (
maven-logging,maven-core)MavenSimpleLogger.LogSink— structured callback with(level, loggerName, cleanMessage, formattedMessage, throwable)replacing the oldConsumer<String>sinkwrite()reuses the existingwriteThrowable()method instead of duplicating rendering logicProjectBuildLogAppenderproducesLogEventobjects (with source metadata when available) instead of raw stringsBuildEventListener.projectLogMessage()now takesLogEventinstead ofStringLog API metadata (
maven-core)DefaultLog.withMetadata()— captures caller class/method/thread via StackWalker, gated behindProjectBuildLogAppender.hasReportCapture()for zero overhead in normal builds (~1-5μs per-call cost only when build report capture is active)Shared with 4.0.x (via forward-port #12929)
The following changes are in the forward-port commit and are not duplicated in this PR's diff:
Log.trace()— default no-op methods preventingAbstractMethodErrorLog.child(name)— hierarchical sub-loggersmaven.mojo.id) — fork-aware save/restoreDefaultLogwarn bug fix +isXxxEnabled()guardsDefaultLogTest— 6 tests (warn regression, metadata lifecycle, trace delegation, trace no-op, child logger, default trace backward compat)PR chain
mvnlogviewerRelated
maven-4.0.x(milestone 4.1.0)Test plan
DefaultLogTest— 6 tests: warn/supplier regression, metadata lifecycle, trace delegation, trace no-op, child logger, default trace backward compatMavenJulHandlerTest— 10 tests: parameterized JUL→SLF4J level mapping, FINEST→TRACE, CONFIG→INFO, metadata null checkMachineBuildEventListenerTest—testProjectLogMessageIncludesExecutionWhenMojoIdPresent: verifiesexecutionJSON field emitted whenmojoIdis set;testProjectLogMessageOmitsExecutionWhenMojoIdAbsent: verifies field absent for project-level log linesmvn test -pl impl/maven-core,impl/maven-logging— all tests pass